# This is a sample Python script.

# Press ⌃R to execute it or replace it with your code.
# Press Double ⇧ to search everywhere for classes, files, tool windows, actions, and settings.
import cv2
import numpy as np

# path = '/Users/wanggh/Desktop/a.jpeg'
# path = '/Users/wanggh/Desktop/b.jpeg'
# path = '/Users/wanggh/Desktop/c.jpeg'
# path = '/Users/wanggh/Desktop/d.jpeg'
# path = '/Users/wanggh/Desktop/e.jpeg'
import point

path = '/Users/wanggh/Desktop/original.jpeg'


# path = '/Users/wanggh/Desktop/b.jpeg'

# 固定尺寸
def resizeImg(image, height=900):
    h, w = image.shape[:2]
    pro = height / h
    size = (int(w * pro), int(height))
    img = cv2.resize(image, size)
    return img


# 边缘检测
def getCanny(image):
    # 高斯模糊
    binary = cv2.GaussianBlur(image, (3, 3), 2, 2)
    # 边缘检测
    binary = cv2.Canny(binary, 60, 240, apertureSize=3)
    # 膨胀操作，尽量使边缘闭合
    kernel = np.ones((5, 5), np.uint8)
    binary = cv2.dilate(binary, kernel, iterations=1)
    return binary


def findMaxContour(image):
    # 寻找边缘
    contours, _ = cv2.findContours(image, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)
    # 计算面积
    max_area = 0.0
    max_contour = []
    lt_coordinate = True
    rt_coordinate = True
    lb_coordinate = True
    rb_coordinate = True
    point_dict = {}
    for contour in contours:
        hull = cv2.convexHull(contour)
        epsilon = 0.02 * cv2.arcLength(contour, True)
        approx = cv2.approxPolyDP(hull, epsilon, True)
        if len(approx) == 4:
            currentArea = cv2.contourArea(approx)
            if 2400 < currentArea < 4000:
                x, y, w, h = cv2.boundingRect(approx)
                p = point.Point(x, y, w, h)
                # cv.rectangle(src, (x, y), (x + w, y + h), (0, 255, 0), 2)
                if x <= 675:
                    if y <= 900 and lt_coordinate:
                        point_dict['lt'] = p
                        lt_coordinate = False
                    elif lb_coordinate:
                        point_dict['lb'] = p
                        lb_coordinate = False
                else:
                    if y <= 900 and rt_coordinate:
                        point_dict['rt'] = p
                        rt_coordinate = False
                    elif rb_coordinate:
                        point_dict['rb'] = p
                        rb_coordinate = False
    lt = point_dict['lt']
    lb = point_dict['lb']
    rt = point_dict['rt']
    rb = point_dict['rb']
    return lt, lb, rt, rb


def getBoxPoint(contour):
    # 多边形拟合凸包
    hull = cv2.convexHull(contour)
    epsilon = 0.02 * cv2.arcLength(contour, True)
    approx = cv2.approxPolyDP(hull, epsilon, True)
    approx = approx.reshape((len(approx), 2))
    return approx


# 代码承接上文
# 适配原四边形点集
def adaPoint(box, pro):
    box_pro = box
    if pro != 1.0:
        box_pro = box / pro
    box_pro = np.trunc(box_pro)
    return box_pro


# 四边形顶点排序，[top-left, top-right, bottom-right, bottom-left]
def orderPoints(pts):
    rect = np.zeros((4, 2), dtype="float32")
    s = pts.sum(axis=1)
    rect[0] = pts[np.argmin(s)]
    rect[2] = pts[np.argmax(s)]
    diff = np.diff(pts, axis=1)
    rect[1] = pts[np.argmin(diff)]
    rect[3] = pts[np.argmax(diff)]
    return rect


# 计算长宽
def pointDistance(a, b):
    return int(np.sqrt(np.sum(np.square(a - b))))


# 透视变换
def warpImage(image, box):
    w, h = pointDistance(box[0], box[1]), \
           pointDistance(box[1], box[2])
    dst_rect = np.array([[0, 0],
                         [w - 1, 0],
                         [w - 1, h - 1],
                         [0, h - 1]], dtype='float32')
    M = cv2.getPerspectiveTransform(box, dst_rect)
    warped = cv2.warpPerspective(image, M, (w, h))
    return warped


image = cv2.imread(path)
ratio = 900 / image.shape[0]
img = resizeImg(image)
binary_img = getCanny(img)
max_contour, max_area = findMaxContour(binary_img)
boxes = getBoxPoint(max_contour)
boxes = adaPoint(boxes, ratio)
boxes = orderPoints(boxes)
# 透视变化
warped = warpImage(image, boxes)

cv2.imshow("test", warped)
cv2.waitKey(0)
cv2.destroyAllWindows()
